﻿using Engine.Graphics.VertexStructures;
using SharpDX.Direct3D11;
using System.Runtime.InteropServices;

namespace Engine.Graphics
{
    public class StandardGraphicBuffers
    {
        #region Public Fields

        public Device device;
        public bool Disposed;

        #endregion Public Fields

        #region Private Fields

        private SharpDX.Direct3D11.Buffer indexBuffer;
        private BufferDescription indexBufferDescription;

        /// <summary>
        /// Positions and Normals
        /// </summary>
        private SharpDX.Direct3D11.Buffer vertexBuffer;

        private BufferDescription vertexBufferDescription;

        /// <summary>
        /// Wr2 Data
        /// </summary>
        private SharpDX.Direct3D11.Buffer vertexBufferWr2Data;

        private BufferDescription vertexBufferWr2DataDescription;

        #endregion Private Fields

        #region Public Constructors

        public StandardGraphicBuffers(Device device) : this()
        {
            this.device = device;
        }

        #endregion Public Constructors

        #region Private Constructors

        private StandardGraphicBuffers()
        {
            vertexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.VertexBuffer,
                CpuAccessFlags = CpuAccessFlags.None,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default
            };
            vertexBufferWr2DataDescription = new BufferDescription()
            {
                BindFlags = BindFlags.VertexBuffer,
                CpuAccessFlags = CpuAccessFlags.None,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default
            };
            indexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.IndexBuffer,
                CpuAccessFlags = CpuAccessFlags.None,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default,
                OptionFlags = ResourceOptionFlags.None
            };

            this.Disposed = true;
        }

        #endregion Private Constructors

        #region Public Properties

        public SharpDX.Direct3D11.Buffer IndexBuffer
        {
            get { return indexBuffer; }
            set { indexBuffer = value; }
        }

        /// <summary>
        /// Vertexbuffer for position and normal Data
        /// </summary>
        public SharpDX.Direct3D11.Buffer VertexBuffer
        {
            get { return vertexBuffer; }
            set { vertexBuffer = value; }
        }

        /// <summary>
        /// Buffer with wr2 Data for vertices
        /// </summary>
        public SharpDX.Direct3D11.Buffer VertexBufferWr2Data
        {
            get { return vertexBufferWr2Data; }
            set { vertexBufferWr2Data = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public void Dispose()
        {
            if (this.indexBuffer != null)
            {
                this.indexBuffer.Dispose();
                this.indexBuffer = null;
            }
            if (this.vertexBuffer != null)
            {
                this.vertexBuffer.Dispose();
                this.vertexBuffer = null;
            }
        }

        public void UpdateVertexBuffer<T>(MyOwnVertex.PositionNormalTextured[] vertices, T[] wr2VertexData) where T : struct
        {
            UpdateVertexBuffer(vertices);
            UpdateVertexBufferGameData(wr2VertexData);
        }

        public void UpdateVertexBuffer(MyOwnVertex.PositionNormalTextured[] vertices)
        {
            if (vertices.Length == 0)
                return;

            int size = MyOwnVertex.PositionNormalTextured.SizeInBytes() * vertices.Length;

            if (this.VertexBuffer == null || size != this.vertexBufferDescription.SizeInBytes)
            {
                Buffer tempBuffer = this.vertexBuffer;

                this.vertexBufferDescription.SizeInBytes = size;

                lock (this.device.ImmediateContext)
                {
                    Buffer newVertexBuffer = Buffer.Create(this.device, vertices, vertexBufferDescription);
                    newVertexBuffer.DebugName = "VertexBuffer of StandardGraphicBuffer Pos Normal";
                    this.vertexBuffer = newVertexBuffer;
                }

                if (tempBuffer != null)
                {
                    tempBuffer.Dispose();
                    tempBuffer = null;
                }
            }
            else
                lock (this.device.ImmediateContext)
                    device.ImmediateContext.UpdateSubresource(vertices, this.VertexBuffer);

            Disposed = false;
        }

        public void UpdateVertexBuffer<T>(T[] vertices) where T : struct
        {
            if (vertices.Length == 0)
                return;

            int size = Marshal.SizeOf(vertices[0].GetType()) * vertices.Length;

            if (this.VertexBuffer == null || size != this.vertexBufferDescription.SizeInBytes)
            {
                Buffer tempBuffer = this.vertexBuffer;

                this.vertexBufferDescription.SizeInBytes = size;

                lock (this.device.ImmediateContext)
                {
                    Buffer newVertexBuffer = Buffer.Create(this.device, vertices, vertexBufferDescription);
                    newVertexBuffer.DebugName = "VertexBuffer of StandardGraphicBuffer Pos Normal";
                    this.vertexBuffer = newVertexBuffer;
                }

                if (tempBuffer != null)
                {
                    tempBuffer.Dispose();
                    tempBuffer = null;
                }
            }
            else
                lock (this.device.ImmediateContext)
                    device.ImmediateContext.UpdateSubresource(vertices, this.VertexBuffer);

            Disposed = false;
        }

        public void UpdateVertexBufferGameData<T>(T[] vertices) where T : struct
        {
            if (vertices.Length == 0)
                return;

            int size = Marshal.SizeOf(typeof(T)) * vertices.Length;

            if (this.VertexBufferWr2Data == null || size != this.vertexBufferWr2DataDescription.SizeInBytes)
            {
                Buffer tempBuffer = this.VertexBufferWr2Data;

                this.vertexBufferDescription.SizeInBytes = size;

                lock (this.device.ImmediateContext)
                {
                    Buffer newVertexBuffer = Buffer.Create(this.device, vertices, vertexBufferDescription);
                    newVertexBuffer.DebugName = "VertexBuffer of StandardGraphicBuffer Wr2 Data";
                    this.VertexBufferWr2Data = newVertexBuffer;
                }

                if (tempBuffer != null)
                {
                    tempBuffer.Dispose();
                    tempBuffer = null;
                }
            }
            else
                lock (this.device.ImmediateContext)
                    device.ImmediateContext.UpdateSubresource(vertices, this.VertexBufferWr2Data);

            Disposed = false;
        }

        #endregion Public Methods

        #region Internal Methods

        internal void UpdateIndexBuffer(ushort[] indices)
        {
            if (indices.Length <= 0)
                return;
            int newSize = indices.Length * sizeof(short);

            if (this.indexBuffer == null || newSize != this.indexBuffer.Description.SizeInBytes)
            {
                this.indexBufferDescription.SizeInBytes = newSize;

                Buffer tempIndexBuffer = this.indexBuffer;
                lock (this.device.ImmediateContext)
                {
                    Buffer newIndexBuffer = Buffer.Create(this.device, indices, indexBufferDescription);
                    newIndexBuffer.DebugName = "VertexBuffer of StandardGraphicBuffer";
                    this.indexBuffer = newIndexBuffer;
                }

                if (tempIndexBuffer != null)
                    tempIndexBuffer.Dispose();
                tempIndexBuffer = null;
            }
            else
                lock (this.device.ImmediateContext)
                {
                    //lock (this.indexBuffer)
                    this.device.ImmediateContext.UpdateSubresource(indices, this.indexBuffer);
                }
        }

        #endregion Internal Methods
    }
}